from math import sin, cos, pi, exp, log
from evaler import eval_expr, subs

MODE = 'set'


def scale(points, x, y, start=0):
    for p in points[start:]:
        p[0] *= float(x) / 100
        p[1] *= float(y) / 100
    return points


def origin(points, x, y, start=0):
    for p in points[start:]:
        p[0] += x
        p[1] += y
    return points


def rot(points, rad, start=0):
    for p in points[start:]:
        x = p[0]
        y = p[1]
        p[0] = x * cos(rad) + y * sin(rad)
        p[1] = y * cos(rad) - x * sin(rad)
    return points


def test_transfer():
    points = [
        [0, 0],
        [1, 1]
    ]
    print(scale(points, 200, 100))
    print(origin(points, 2, 2))
    print(rot(points, pi))

# built-in functions

TRANS = {
    'scale': [100, 100],
    'rot': 0,
    'origin': [0, 0]
}


def trans(points, start):
    points = scale(points, TRANS['scale'][0], TRANS['scale'][1], start=start)
    points = rot(points, TRANS['rot'], start=start)
    points = origin(points, TRANS['origin'][0], TRANS['origin'][1], start=start)
    return points


def handle_for(tokens, points, mode=MODE):
    # for VAR from START to STOP step STEP draw(X,Y)
    former_len = len(points)
    var = tokens[1]
    i = 2
    tags = []
    while i < len(tokens):
        if tokens[i] in {'from', 'to', 'step', 'draw'}:
            tags.append(i)
        i += 1
    if len(tags) != 4:
        raise RuntimeError('ERROR_WRONG_SYNTAX')
    # get params
    beg = tags[0] + 1
    end = tags[1]
    start = eval_expr(tokens[beg:end])
    beg = tags[1] + 1
    end = tags[2]
    stop = eval_expr(tokens[beg:end])
    beg = tags[2] + 1
    end = tags[3]
    step = eval_expr(tokens[beg:end])
    i = tags[3]
    while i < len(tokens):
        if tokens[i] == ',':
            expr_x = tokens[tags[3] + 2:i]
            expr_y = tokens[i+1:-1]
            break
        i += 1
    i = start
    while i < stop:
        x = subs(expr_x, var, i)
        y = subs(expr_y, var, i)
        points.append([x, y])
        i += step
    if mode == 'set':
        points = trans(points, former_len)
    return points


def handle_scale(tokens, points, mode=MODE):
    # scale is (x, y)
    start = -1
    i = 0
    while i < len(tokens):
        if tokens[i] == 'is':
            start = i + 2
            i += 1  # skip '('
        if tokens[i] == ',':
            x = eval_expr(tokens[start:i])
            y = eval_expr(tokens[i+1:-1])
            if mode == 'do':
                points = scale(points, x, y)
            elif mode == 'set':
                TRANS['scale'] = [x, y]
            return points
        i += 1
    raise RuntimeError('ERROR_WRONG_SYNTAX')


def handle_origin(tokens, points, mode=MODE):
    # origin is (x,y)
    start = -1
    i = 0
    while i < len(tokens):
        if tokens[i] == 'is':
            start = i + 2
            i += 1  # skip '('
        if tokens[i] == ',':
            x = eval_expr(tokens[start:i])
            y = eval_expr(tokens[i+1:-1])
            if mode == 'do':
                points = origin(points, x, y)
            elif mode == 'set':
                TRANS['origin'] = [x, y]
            return points
        i += 1
    raise RuntimeError('ERROR_WRONG_SYNTAX')


def handle_rot(tokens, points, mode=MODE):
    # rot is x
    i = 0
    while i < len(tokens):
        if tokens[i] == 'is':
            rad = eval_expr(tokens[i+1:])
            if mode == 'do':
                points = rot(points, rad)
            elif mode == 'set':
                TRANS['rot'] = rad
            return points
        i += 1
    raise RuntimeError('ERROR_WRONG_SYNTAX')


COMMANDS = {
    'for': handle_for,
    'scale': handle_scale,
    'origin': handle_origin,
    'rot': handle_rot,
}


# main parser


def parse(tokens):
    points = []
    i = 0
    start = -1
    cmd = ''
    while i < len(tokens):
        if tokens[i] in COMMANDS.keys():
            cmd = tokens[i]
            start = i
        elif tokens[i] == ';':
            method = COMMANDS[cmd]
            points = method(tokens[start:i], points)
        i += 1
    return points

